%% -*- coding: utf-8 -*-

%% @doc 式のテスト
%%
-module(t_templel).
-import(templ_env, [init_vtable/1]).

-include_lib("eunit/include/eunit.hrl").

templel_eval(EL) ->
	Ts = templel_scan:string(EL),
	{ok,Val} = templel_parser:parse(Ts),
	Val.

templel_eval(VTable, EL) ->
	Val = templel_eval(EL),
	case is_function(Val) of
		true  -> Val(VTable);
		false -> Val
	end.
			

eval_test_() ->
	[?_assertMatch(123, 
				   templel_eval("123"))
	].

boolean_test_() ->
	[?_assertMatch(true, 
				   templel_eval("true")),
	 ?_assertMatch(true, 
				   templel_eval("true || false && true==false")),
	 ?_assertMatch(false, 
				   templel_eval("(true || false) && true!=true")),
	 ?_assertMatch(false, 
				   templel_eval("!(true || false) || true!=true")),
	 ?_assertMatch(true, 
				   templel_eval("123 == 123 && 123 != 456 && 
                                 123 < 456 && 123 <= 123 &&
                                 456 > 123 && 123 >= 123")),

	 ?_assertThrow({{templel_parse_handler,einval},{true,1}}, 
				   templel_eval("true || 1")),
	 ?_assertThrow({{templel_parse_handler,einval},{true,"true"}}, 
				   templel_eval("true && \"true\"")),
	 ?_assertThrow({{templel_parse_handler,einval},{1,"true"}}, 
				   templel_eval("1 && \"true\"")),

	 ?_assertMatch(false, 
				   templel_eval("0 == 1 || 0 != 0 || 
                                 1 < 0 || 0 < 0 || 1 <= 0 || 
                                 0 > 1 || 0 > 0 || 0 >= 1")),
	 ?_assertMatch(true,
				   templel_eval("0 == 0 && 0 == -0 && -1 < 1")),
	 ?_assertMatch(true,
				   templel_eval("\"abc\" == \"abc\" && \"a\" != \"\" && \"\\u3299\" == \"㊙\"")), % "
	 ?_assertMatch(false,
				   templel_eval("true == 0 || true == \"true\" || 123 == \"123\"")),
	 ?_assertMatch(true,
				   templel_eval("true != 1 && true != \"false\" && 0 != \"1\"")),

	 ?_assertThrow({{templel_parse_handler,einval},{false,true}}, 
				   templel_eval("false < true")),
	 ?_assertThrow({{templel_parse_handler,einval},{0,true}},
				   templel_eval("0 <= true")),
	 ?_assertThrow({{templel_parse_handler,einval},{false,"true"}}, 
				   templel_eval("false < \"true\""))
	].

array_test_() ->
	VT = init_vtable([{"names",["John","Gary","Gregg","James"]},
					  {"title","My Homepage"},
					  {"ten", 10}, {"bool", true},
					  {"empty", []}]),

	[?_assertMatch(["John","Gary","Gregg","James"],
				   (templel_eval("names"))(VT)),
	 ?_assertMatch("John",
				   (templel_eval("names[1]"))(VT)),

	 ?_assertThrow({{templel_parse_handler,einval},{"names",_},"out of bound"}, 
				   (templel_eval("names[0]"))(VT)),
	 ?_assertThrow({{templel_parse_handler,einval},{"names",_},"out of bound"}, 
				   (templel_eval("names[10]"))(VT)),
	 ?_assertThrow({{templel_parse_handler,einval},{"names",_},"out of bound"}, 
				   (templel_eval("names[-1]"))(VT)),

	 ?_assertMatch("John",
				   (templel_eval("names.1"))(VT)),

	 ?_assertMatch(77,
				   (templel_eval("title[1]"))(VT)),
	 ?_assertMatch(121,
				   (templel_eval("title[2]"))(VT)),

	 ?_assertThrow({{templel_parse_handler,einval},{"title","My Homepage"},"out of bound"}, 
				   (templel_eval("title[20]"))(VT)),
	 ?_assertThrow({{templel_parse_handler,einval},{{value,"ten",10},1}},
				   (templel_eval("ten[1]"))(VT)),
	 ?_assertThrow({{templel_parse_handler,einval},{{value,"bool",true},1}},
				   (templel_eval("bool[1]"))(VT)),

	 ?_assertMatch("",
				   (templel_eval("empty"))(VT))
	].

arith_test_() ->
	VT = init_vtable([{"foo",10}, {"bar",0}]),

	[?_assertMatch(12, 
				   templel_eval(VT, "10+1*2")),
	 ?_assertMatch(11,
				   templel_eval(VT, "foo+1")),
	 ?_assertMatch(0,
				   templel_eval(VT, "foo * bar")),
	 ?_assertMatch(122,
				   templel_eval(VT, "foo*2+10*(bar+10)+2"))
	].

erlangcall_test_() ->
	VT = init_vtable([{"module",erlang}, 
					  {"l2a",list_to_atom}, {"l2i",list_to_integer},
					  {"string",[{"module",string}, {"substr",substr}]},
					  {"arg","hoge"}]),

	[?_assertMatch(hoge, 
				   templel_eval(VT, "erlang:list_to_atom(\"hoge\")")),
	 ?_assertMatch("ge",
				   templel_eval(VT, "string :\n substr (\"hoge\", 3)")),
	 ?_assertError(undef, 
				   templel_eval(VT, "module:l2a(\"hoge\")")),
	 ?_assertError(_,
				   templel_eval(VT, "module:l2\na(\"hoge\")")),
	 ?_assertMatch("ge",
				   templel_eval(VT, "string.module\n : string.substr (\"hoge\", 3)")),
	 ?_assertMatch("ge",
				   templel_eval(VT, "string.module\n : string.substr (arg, 3)"))
	].
